﻿using System;
using System.Collections.Generic;
using UtilZ.Dotnet.AudioPlayer.Base;
using UtilZ.Dotnet.AudioPlayer.Core;
using UtilZ.Dotnet.AudioPlayer.Native;
using UtilZ.Dotnet.Ex.Audio;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.AudioPlayer.Player
{
    /// <summary>
    /// 声音播放器基类
    /// </summary>
    public abstract class SoundPlayerAbs : IDisposable
    {
        #region 静态方法

        /// <summary>
        /// Retrieves information on an output device
        /// </summary>
        /// <param name="deviceId">The device to get the information of... 0 = first. </param>
        /// <returns>If successful, then SoundPlayingDeviceInfo is returned, else null is returned</returns>
        public static SoundPlayingDeviceInfo GetSoundDeviceInfo(int deviceId)
        {
            var info = new BASS_DEVICEINFO_INTERNAL();
            if (BASSNativeMethods.BASS_GetDeviceInfo(deviceId, ref info))
            {
                return new SoundPlayingDeviceInfo(deviceId, ref info);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取当前系统中所有的声音播放设备
        /// </summary>
        /// <returns>当前系统中所有的声音播放设备</returns>
        public static List<SoundPlayingDeviceInfo> GetSoundDeviceInfos()
        {
            var list = new List<SoundPlayingDeviceInfo>();
            //var list2 = new List<SoundPlayingDeviceInfo>();

            var info = new BASS_DEVICEINFO_INTERNAL();
            for (int deviceId = 0; ; deviceId++)
            {
                if (BASSHelper.GetDeviceInfo(deviceId, ref info))
                {
                    list.Add(new SoundPlayingDeviceInfo(deviceId, ref info));

                    //if (NativeMethods.BASS_GetDeviceInfo(i, ref info))
                    //{
                    //    list2.Add(new SoundPlayingDeviceInfo(i, ref info));
                    //}
                }
                else
                {
                    break;
                }
            }

            return list;
        }

        /// <summary>
        /// Sets the device to use for subsequent calls in the current thread
        /// </summary>
        /// <param name="device">The device to use... 0 = no sound, 1 = first real output device</param>
        public static void SetDevice(int device)
        {
            BASSHelper.SetDevice(device);
        }

        /// <summary>
        /// 初始化声音输出设备
        /// </summary>
        /// <param name="device"> -1 = default device, 0 = no sound, 1 = first real output device. BASS_GetDeviceInfo can be used to enumerate the available devices. </param>
        /// <param name="freq">输出率</param>
        /// <param name="flags">A combination of these flags</param>
        public static void InitDevice(int device = -1, int freq = 48000, BASSInit flags = BASSInit.BASS_DEVICE_DEFAULT)
        {
            BASSHelper.Init(device, freq, flags);
        }

        /// <summary>
        /// Frees all resources used by the output device, including all its samples, streams and MOD musics
        /// </summary>
        public static void Free()
        {
            BASSHelper.Free();
        }

        #endregion



        /// <summary>
        /// 播放器标识
        /// </summary>
        protected readonly int _ID;
        /// <summary>
        /// 播放器标识
        /// </summary>
        public int ID
        {
            get { return _ID; }
        }

        /// <summary>
        /// 
        /// </summary>
        protected int _handle = AudioConstant.NONE_HANDLE;
        /// <summary>
        /// 获取bass句柄
        /// </summary>
        public int Handle
        {
            get { return this._handle; }
        }

        /// <summary>
        /// 音频信息
        /// </summary>
        public WaveInfo WaveInfo
        {
            get { return this.GetWaveInfo(); }
        }

        /// <summary>
        /// 获取音频信息
        /// </summary>
        /// <returns></returns>
        protected abstract WaveInfo GetWaveInfo();

        private float? _volume = null;
        /// <summary>
        /// 获取或设置音量[0-1]
        /// </summary>
        public float Volume
        {
            get
            {
                if (this._volume.HasValue)
                {
                    return this._volume.Value;
                }
                else
                {
                    if (this.HandleValid())
                    {
                        return BASSHelper.ChannelGetVolume(this._handle);
                    }
                    else
                    {
                        return 0f;
                    }
                }
            }
            set
            {
                this._volume = value;
                if (this.HandleValid())
                {
                    this.UpdateVolume();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected void UpdateVolume()
        {
            float? volume = this._volume;
            if (volume.HasValue)
            {
                BASSHelper.ChannelSetVolume(this._handle, volume.Value);
            }
        }



        private float? _balance = null;
        /// <summary>
        /// The panning/balance position of a channel[The pan position... -1.0f (full left) to +1.0f (full right), 0.0 = centre]
        /// </summary>
        public float Balance
        {
            get
            {
                if (this._balance.HasValue)
                {
                    return this._balance.Value;
                }
                else
                {
                    if (this.HandleValid())
                    {
                        return BASSHelper.ChannelGetAttribute(this._handle, BASSAttribute.BASS_ATTRIB_PAN);
                    }
                    else
                    {
                        return 0f;
                    }
                }
            }
            set
            {
                this._balance = value;
                if (this.HandleValid())
                {
                    this.UpdateBalance();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected void UpdateBalance()
        {
            float? balance = this._balance;
            if (balance.HasValue)
            {
                BASSHelper.ChannelSetAttribute(this._handle, BASSAttribute.BASS_ATTRIB_PAN, balance.Value);
            }
        }




        private float? _speed = null;
        /// <summary>
        /// 获取或设置播放速度[0.01-100],小于1减速,大于1加速
        /// </summary>
        public float Speed
        {
            get
            {
                if (this._speed.HasValue)
                {
                    return this._speed.Value;
                }
                else
                {
                    if (this.HandleValid())
                    {
                        float playSampleRate = BASSHelper.ChannelGetSpeed(this._handle);
                        var waveInfo = this.WaveInfo;
                        if (waveInfo == null)
                        {
                            throw new AudioException("句柄已释放");
                        }

                        var sampleRate = waveInfo.SampleRate;
                        this._speed = (float)Math.Round(playSampleRate / sampleRate, 3);
                        return this._speed.Value;
                    }
                    else
                    {
                        return 1.0F;
                    }
                }
            }
            set
            {
                if (value < 0.01F || value > 100.0F)
                {
                    throw new ArgumentOutOfRangeException($"播放速度系数值\"{value}\"无效,取值范围0.01-100");
                }

                this._speed = value;
                if (this.HandleValid())
                {
                    this.UpdatePlaySpeed();
                }
            }
        }


        /// <summary>
        /// 
        /// </summary>
        protected void UpdatePlaySpeed()
        {
            float? speed = this._speed;
            if (!speed.HasValue)
            {
                return;
            }

            var waveInfo = this.WaveInfo;
            if (waveInfo == null)
            {
                throw new AudioException("句柄已释放");
            }

            var playSampleRate = waveInfo.SampleRate * speed.Value;
            BASSHelper.ChannelSetSpeed(this._handle, playSampleRate);
        }


        //指定设备报错
        //protected int _device = 1;
        ///// <summary>
        ///// 获取或设置输出设备(0 = no sound, 1 = first real output device, BASS_NODEVICE = no device)
        ///// </summary>
        //public int Device
        //{
        //    get { return this._device; }
        //    set
        //    {
        //        this._device = value;
        //        if (this.HandleValid())
        //        {
        //            WavHelper.ChannelSetDevice(this._handle, this._device);
        //        }
        //    }
        //}


        private SoundPlayerStatus _status = SoundPlayerStatus.Stoped;
        /// <summary>
        /// 获取播放器状态
        /// </summary>
        public SoundPlayerStatus Status
        {
            get
            {
                if (!this.HandleValid() && this._status != SoundPlayerStatus.Stoped)
                {
                    this._status = SoundPlayerStatus.Stoped;
                }

                return this._status;
            }
        }

        /// <summary>
        /// 声音播放器类型
        /// </summary>
        public SoundPlayerType PlayerType { get; private set; }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="soundPlayerType">声音播放器类型</param>
        /// <param name="id">播放器标识</param>
        public SoundPlayerAbs(SoundPlayerType soundPlayerType, int id)
        {
            if (!BASSHelper.Inited)
            {
                throw new InvalidOperationException("bass库未初始化。");
            }

            this.PlayerType = soundPlayerType;
            this._ID = id;
        }




        /// <summary>
        /// 
        /// </summary>
        protected bool HandleValid()
        {
            return this._handle != AudioConstant.NONE_HANDLE;
        }

        /// <summary>
        /// 释放播放器
        /// </summary>
        protected virtual void FreeBASS()
        {
            try
            {
                if (this.HandleValid())
                {
                    BASSHelper.StreamFree(this._handle);
                    this._handle = AudioConstant.NONE_HANDLE;
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(this._ID, null, ex, "FreeBASS异常");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected bool _disposed = false;
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (this._disposed)
            {
                return;
            }

            this._disposed = true;
            this.FreeBASS();

            this.Dispose(true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {

        }











        private bool ValidatePPSOperate(SoundPlayerStatus targetStatus)
        {
            if (this._disposed)
            {
                return false;
            }

            if (!this.HandleValid())
            {
                return false;
                //throw new WavException($"从原始状态{this._status.ToString()}执行{targetStatus.ToString()}操作失败,未加载音频数据");
            }

            if (this._status == targetStatus)
            {
                return false;
            }

            return true;
        }





        /// <summary>
        /// 播放
        /// </summary>
        public void Play()
        {
            if (this.ValidatePPSOperate(SoundPlayerStatus.Playing))
            {
                this._status = SoundPlayerStatus.StartPlaying;
                BASSHelper.ChannelPlay(this._handle, this.Status == SoundPlayerStatus.Stoped);
                this._status = SoundPlayerStatus.Playing;
                this.OnPlayStatusChanged();
            }
        }

        /// <summary>
        /// 暂停
        /// </summary>
        public void Pause()
        {
            if (this.ValidatePPSOperate(SoundPlayerStatus.Paused))
            {
                this._status = SoundPlayerStatus.Pausing;
                BASSHelper.ChannelPause(this._handle);
                this._status = SoundPlayerStatus.Paused;
                this.OnPlayStatusChanged();
            }
        }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            if (this.ValidatePPSOperate(SoundPlayerStatus.Stoped))
            {
                this._status = SoundPlayerStatus.Stoping;
                BASSHelper.ChannelStop(this._handle);
                this._status = SoundPlayerStatus.Stoped;
                this.OnPlayStatusChanged();
            }
        }

        /// <summary>
        /// 播放状态改变通知
        /// </summary>
        protected virtual void OnPlayStatusChanged()
        {

        }




        /// <summary>
        /// 获取音频文件信息
        /// </summary>
        /// <returns>音频文件信息</returns>
        public BASS_CHANNELINFO_INTERNAL GetWavInfo()
        {
            if (this.HandleValid())
            {
                return BASSHelper.ChannelGetInfo(this._handle);
            }
            else
            {
                throw new AudioException("未加载音频数据");
            }
        }

        /// <summary>
        /// 声音淡入淡出
        /// </summary>
        /// <param name="volValue">音量目标值</param>
        /// <param name="duration">持续时间,单位/毫秒</param>
        public void VoiceSlide(float volValue, int duration)
        {
            if (!this.HandleValid())
            {
                return;
            }

            //float value = 0.2f; 
            //int duration=2*1000;
            BASSHelper.ChannelSlideAttribute(this._handle, BASSAttribute.BASS_ATTRIB_VOL, volValue, duration);
        }



        /// <summary>
        /// 获取实时FFT数据,short类型
        /// </summary>
        /// <param name="fftData">FFT数据存放数据</param>
        /// <returns>获取到的FFT数据长度</returns>
        public int GetFFTDataShort(short[] fftData)
        {
            if (this.HandleValid())
            {
                return BASSHelper.ChannelGetData(this._handle, fftData, fftData.Length * 2);
            }
            else
            {
                throw new ArgumentException("句柄无效");
            }
        }

        /// <summary>
        /// 获取实时FFT数据,float类型
        /// </summary>
        /// <param name="fftData">FFT数据存放数据</param>
        /// <returns>获取到的FFT数据长度</returns>
        public int GetFFTDataFloat(float[] fftData)
        {
            if (this.HandleValid())
            {
                return BASSHelper.ChannelGetData(this._handle, fftData, fftData.Length * 4);
            }
            else
            {
                throw new ArgumentException("句柄无效");
            }
        }
    }
}
